/**
 * Class: Behavior
 *
 * Purpose: Behavior are attached to Particles to create time-based actions
 *
 * Created: Feb 5, 2010 @ 7:40:29 AM
 * @author Ken Scott
 */
package org.openfantasia.ps.modifier;

import org.openfantasia.ps.modifier.behaviors.Condition;
import org.openfantasia.ps.modifier.behaviors.Timeframe;
import org.openfantasia.ps.particle.Particle;
import org.openfantasia.util.listener.Event;
import org.openfantasia.util.listener.Listener;

import java.util.ArrayList;
import java.util.List;

public class Behavior implements Listener, Cloneable {

	public static final String LINE_COLOR = "lineColor";

	public static final String LINE_WEIGHT = "lineWeight";

	public static final String FILL_COLOR = "fillColor";

	public static final String VISIBILITY = "visibility";

	public static final String SIZE = "size";

	public static final String POSITION = "position";

	public static final String ORIENTATION = "orientation";

	public static final String VELOCITY = "velocity";

	public static final String ACCELERATION = "acceleration";

	public static final String MASS = "mass";

	public static final String OPACITY = "opacity";

	protected Double startTime = Double.MAX_VALUE;

	protected Double endTime = 0d;

	protected List<Condition> conditions = new ArrayList<Condition>();

	protected Object parameter;


	public Behavior() {
	}


	public Behavior(Double startTime, Double endTime) {
		addCondition(new Timeframe(startTime, endTime));
	}


	public Behavior(Condition condition) {
		addCondition(condition);
	}


	public Behavior(List<Timeframe> conditions) {
		for (Condition condition : conditions) {
			addCondition(condition.copy());
		}
	}


	public Behavior(Behavior that) {
		copy(that);
	}


	public Behavior copy() {
		Behavior result = null;
		try {
			result = (Behavior) clone();
			result.conditions = new ArrayList<Condition>();
			result.parameter = null;
		}
		catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return result;
	}


	public Behavior copy(Double startTime, Double endTime) {
		Behavior result = null;
		try {
			result = (Behavior) clone();
			result.conditions = new ArrayList<Condition>();
			result.parameter = null;
			result.addTimeframe(startTime, endTime);
		}
		catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return result;
	}


	public Behavior copy(Double startTime, Double endTime, Object param) {
		Behavior result = null;
		try {
			result = (Behavior) clone();
			result.conditions = new ArrayList<Condition>();
			result.parameter = null;
			result.addTimeframe(startTime, endTime);
			result.parameter = param;
		}
		catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return result;
	}


	public Behavior copy(Condition condition, Object param) {
		Behavior result = null;
		try {
			result = (Behavior) clone();
			result.conditions = new ArrayList<Condition>();
			result.parameter = null;
			result.addCondition(condition);
			result.parameter = param;
		}
		catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return result;
	}


	public void copy(Behavior that) {
		clear();
		for (Condition tf : that.conditions) {
			addCondition((Condition) tf.copy());
		}
		this.parameter = that.parameter;
	}


	public Object getValue(Particle particle, Double time, Object param) {
		throw new RuntimeException("getValue() has not been implemented");
	}


	public void clear() {
		conditions.clear();
		startTime = 0d;
		endTime = 0d;
		parameter = null;
	}


	public void addTimeframe(Double start, Double end) {
		Timeframe tf = new Timeframe(start, end);
		addCondition(tf);
	}


	public void addCondition(Condition condition) {
		if (!conditions.contains(condition)) {
			conditions.add(condition);

			if (condition instanceof Timeframe) {
				Timeframe tf = (Timeframe) condition;
				if (tf.getStartTime() < startTime) {
					startTime = tf.getStartTime();
				}
				if (tf.getEndTime() > endTime) {
					endTime = tf.getEndTime();
				}
			}
		}
	}


	public boolean isActive(Particle particle, Object param) {
		return getActiveCondition(particle, param) != null;
	}


	protected Condition getActiveCondition(Particle particle, Object param) {
		Condition result = null;

		for (int i = 0; (result == null) && (i < conditions.size()); i++) {
			Condition condition = conditions.get(i);
			if (condition.isActive(particle, param)) {
				result = condition;
			}
		}

		return result;
	}


	public double getBehaviorTime(Particle particle, double time) {
		double result = 0;

		Condition condition = getActiveCondition(particle, time);
		if (condition != null) {
			result = ((Timeframe) condition).getInternalTime(time);
		}

		return result;
	}


	public double getBehaviorDuration() {
		return (endTime - startTime);
	}


	public double getBehaviorTimeFraction(Particle particle, double time) {
		double result = 0;

		Condition condition = getActiveCondition(particle, time);
		if (condition != null) {
			result = ((Timeframe) condition).getInternalFraction(time);
		}

		return result;
	}


	public Object getParameter() {
		return parameter;
	}


	public void setParameter(Object parameter) {
		this.parameter = parameter;
	}


	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof Behavior)) {
			return false;
		}

		Behavior behavior = (Behavior) o;

		if (conditions != null ? !conditions.equals(behavior.conditions) : behavior.conditions != null) {
			return false;
		}

		return true;
	}


	@Override
	public int hashCode() {
		return conditions != null ? conditions.hashCode() : 0;
	}


	public void receive(Event event) {

	}


	@Override
	public String toString() {
		return "Behavior{" +
				"startTime=" + startTime +
				", endTime=" + endTime +
				", conditions=" + conditions +
				'}';
	}
}
